using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using Lua511;
using KopiLua;
using LuaInterfaceHelper;

namespace LuaInterface
{	
    /*
     * Main class of LuaInterface
     * Object-oriented wrapper to Lua API
     *
     * Author: Fabio Mascarenhas
     * Version: 1.0
     *
     * // steffenj: important changes in Lua class:
     * - removed all Open*Lib() functions
     * - all libs automatically open in the Lua class constructor (just assign nil to unwanted libs)
     * */
    public static class LuaI
    {
        static string init_luanet =
        @"local metatable = {}
        local rawget = rawget
        local import_type = luanet.import_type
        local load_assembly = luanet.load_assembly
        luanet.error, luanet.type = error, type
        -- Lookup a .NET identifier component.
        function metatable:__index(key) -- key is e.g. 'Form'
            -- Get the fully-qualified name, e.g. 'System.Windows.Forms.Form'
            local fqn = rawget(self,'.fqn')
            fqn = ((fqn and fqn .. '.') or '') .. key

            -- Try to find either a luanet function or a CLR type
            local obj = rawget(luanet,key) or import_type(fqn)

            -- If key is neither a luanet function or a CLR type, then it is simply
            -- an identifier component.
            if obj == nil then
                -- It might be an assembly, so we load it too.
                pcall(load_assembly,fqn)
                obj = { ['.fqn'] = fqn }
                setmetatable(obj, metatable)
            end

            -- Cache this lookup
            rawset(self, key, obj)
            return obj
        end

        -- A non-type has been called; e.g. foo = System.Foo()
        function metatable:__call(...)
            error('No such type: ' .. rawget(self,'.fqn'), 2)
        end

        -- This is the root of the .NET namespace
        luanet['.fqn'] = false
        setmetatable(luanet, metatable)

        -- Preload the mscorlib assembly
        luanet.load_assembly('mscorlib')";



        public static Lua.lua_State init()
        {
        	LuaIContext ctx = new LuaIContext();
        	Lua.lua_State ctxluaState;
            ctxluaState = Lua.luaL_newstate();	// steffenj: Lua 5.1.1 API change (lua_open is gone)
            //LuaDLL.luaopen_base(luaState);	// steffenj: luaopen_* no longer used
            Lua.luaL_openlibs(ctxluaState);		// steffenj: Lua 5.1.1 API change (luaopen_base is gone, just open all libs right here)
            Lua.lua_pushstring(ctxluaState, "LUAINTERFACE LOADED");
            Lua.lua_pushboolean(ctxluaState, 1);
            Lua.lua_settable(ctxluaState, Lua.LUA_REGISTRYINDEX);
            Lua.lua_newtable(ctxluaState);
            Lua.lua_setglobal(ctxluaState, "luanet");
            Lua.lua_pushvalue(ctxluaState, Lua.LUA_GLOBALSINDEX);
            Lua.lua_getglobal(ctxluaState, "luanet");
            Lua.lua_pushstring(ctxluaState, "getmetatable");
            Lua.lua_getglobal(ctxluaState, "getmetatable");
            Lua.lua_settable(ctxluaState, -3);
            Lua.lua_replace(ctxluaState, Lua.LUA_GLOBALSINDEX);
            ctx.translator=new ObjectTranslator(ctx, ctxluaState);
            Lua.lua_replace(ctxluaState, Lua.LUA_GLOBALSINDEX);
            Lua.luaL_dostring(ctxluaState, init_luanet);	// steffenj: lua_dostring renamed to luaL_dostring

            ctx.tracebackFunction = new Lua.lua_CFunction(traceback);

            // We need to keep this in a managed reference so the delegate doesn't get garbage collected
            ctx.panicCallback = new Lua.lua_CFunction(PanicCallback);
            Lua.lua_atpanic(ctxluaState, ctx.panicCallback);
            
            setLuaIContext(ctxluaState, ctx);
            //LuaIContext ctx2 = getLuaIContext(ctxluaState);
            //Console.WriteLine("ctx2 == " + ctx2);
            
			return ctxluaState;
        }
        
        public static void setLuaIContext(Lua.lua_State ctxluaState, LuaIContext ctx)
        {
            Lua.lua_pushstring(ctxluaState, "LUAINTERFACE CTX");
            Lua.lua_pushlightuserdata(ctxluaState, ctx);
            Lua.lua_settable(ctxluaState, Lua.LUA_REGISTRYINDEX);
		}
        
        public static LuaIContext getLuaIContext(Lua.lua_State ctxluaState)
        {
            Lua.lua_pushstring(ctxluaState, "LUAINTERFACE CTX");
            Lua.lua_gettable(ctxluaState, Lua.LUA_REGISTRYINDEX);     
            object result = Lua.lua_topointer(ctxluaState, -1);
            if (result == null)
            {
            	Lua.lua_settop(ctxluaState, -2);
            	throw new LuaException("There is no LuaIContext instance associated with this Lua state");
            }
            Lua.lua_settop(ctxluaState, -2);
            return (LuaIContext)result;
		}

        /*
         * CAUTION: LuaInterface.Lua instances can't share the same lua state!
         */
        public static Lua.lua_State init(LuaIContext ctx, Lua.lua_State lState)
        {
        	Lua.lua_State ctxluaState = null;
            //IntPtr lState = new IntPtr(luaState);
            Lua.lua_pushstring(lState, "LUAINTERFACE LOADED");
            Lua.lua_gettable(lState, Lua.LUA_REGISTRYINDEX);

            if(Lua.lua_toboolean(lState,-1) != 0)
            {
                Lua.lua_settop(lState,-2);
                throw new LuaException("There is already a LuaInterface.Lua instance associated with this Lua state");
            }
            else
            {
                Lua.lua_settop(lState,-2);
                Lua.lua_pushstring(lState, "LUAINTERFACE LOADED");
                Lua.lua_pushboolean(lState, 1);
                Lua.lua_settable(lState, Lua.LUA_REGISTRYINDEX);
                ctxluaState=lState;
                Lua.lua_pushvalue(lState, Lua.LUA_GLOBALSINDEX);
                Lua.lua_getglobal(lState, "luanet");
                Lua.lua_pushstring(lState, "getmetatable");
                Lua.lua_getglobal(lState, "getmetatable");
                Lua.lua_settable(lState, -3);
                Lua.lua_replace(lState, Lua.LUA_GLOBALSINDEX);
                ctx.translator=new ObjectTranslator(ctx, ctxluaState);
                Lua.lua_replace(lState, Lua.LUA_GLOBALSINDEX);
                Lua.luaL_dostring(lState, init_luanet);	// steffenj: lua_dostring renamed to luaL_dostring
            }

            ctx._StatePassed = true;
            return ctxluaState;
        }

        public static void Close(Lua.lua_State ctxluaState, LuaIContext ctx)
        {
            if (ctx._StatePassed)
                return;

            if (ctxluaState != null)
                Lua.lua_close(ctxluaState);
            //luaState = IntPtr.Zero; <- suggested by Christopher Cebulski http://luaforge.net/forum/forum.php?thread_id=44593&forum_id=146
        }

        /// <summary>
        /// Called for each lua_lock call 
        /// </summary>
        /// <param name="luaState"></param>
        /// Not yet used
        public static int LockCallback(Lua.lua_State luaState)
        {
            return 0;
        }

        /// <summary>
        /// Called for each lua_unlock call 
        /// </summary>
        /// <param name="luaState"></param>
        /// Not yet used
        public static int UnlockCallback(Lua.lua_State luaState)
        {
            // Monitor.Exit(luaLock);

            return 0;
        }

        static int PanicCallback(Lua.lua_State luaState)
        {
            // string desc = LuaDLL.lua_tostring(luaState, 1);

            // string desc = LuaDLL.lua_tostring(luaState, 1);
            string reason = String.Format("unprotected error in call to Lua API ({0})", Lua.lua_tostring(luaState, -1)); //luanet_

           //        lua_tostring(L, -1);

            throw new LuaException(reason);
        }



        /// <summary>
        /// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app
        /// </summary>
        /// <exception cref="LuaScriptException">Thrown if the script caused an exception</exception>
        public static void ThrowExceptionFromError(Lua.lua_State ctxluaState, LuaIContext ctx, int oldTop)
        {
            object err = ctx.translator.getObject(ctxluaState, -1);
            Lua.lua_settop(ctxluaState, oldTop);

            // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
            LuaException luaEx = err as LuaException;
            if (luaEx != null) throw luaEx;

            // A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it
            if (err == null) err = "Unknown Lua Error";
            throw new LuaException(err.ToString());
        }



        /// <summary>
        /// Convert C# exceptions into Lua errors
        /// </summary>
        /// <returns>num of things on stack</returns>
        /// <param name="e">null for no pending exception</param>
        internal static int SetPendingException(Lua.lua_State ctxluaState, LuaIContext ctx, Exception e)
        {
            Exception caughtExcept = e;

            if (caughtExcept != null)
            {
                ctx.translator.throwError(ctxluaState, caughtExcept);
                Lua.lua_pushnil(ctxluaState);

                return 1;
            }
            else
                return 0;
        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static LuaFunction LoadString(Lua.lua_State ctxluaState, LuaIContext ctx, string chunk, string name)
        {
            int oldTop = Lua.lua_gettop(ctxluaState);

            ctx.executing = true;
            try
            {
            	if (Lua.luaL_loadbuffer(ctxluaState, chunk, (uint)chunk.Length, name) != 0)
                    ThrowExceptionFromError(ctxluaState, ctx, oldTop);
            }
            finally { ctx.executing = false; }

            LuaFunction result = ctx.translator.getFunction(ctxluaState, -1);
            ctx.translator.popValues(ctxluaState, oldTop);

            return result;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static LuaFunction LoadFile(Lua.lua_State ctxluaState, LuaIContext ctx, string fileName)
        {
            int oldTop = Lua.lua_gettop(ctxluaState);
            if (Lua.luaL_loadfile(ctxluaState, fileName) != 0)
                ThrowExceptionFromError(ctxluaState, ctx, oldTop);

            LuaFunction result = ctx.translator.getFunction(ctxluaState, -1);
            ctx.translator.popValues(ctxluaState, oldTop);

            return result;
        }


        /*
         * Excutes a Lua chunk and returns all the chunk's return
         * values in an array
         */
        public static object[] DoString(Lua.lua_State ctxluaState, string chunk)
        {
            return DoString(ctxluaState, chunk, "chunk");
        }

        /// <summary>
        /// Executes a Lua chnk and returns all the chunk's return values in an array.
        /// </summary>
        /// <param name="chunk">Chunk to execute</param>
        /// <param name="chunkName">Name to associate with the chunk</param>
        /// <returns></returns>
        public static object[] DoString(Lua.lua_State ctxluaState, string chunk, string chunkName)
        {
        	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");   
            int oldTop = Lua.lua_gettop(ctxluaState);
            ctx.executing = true;
            if (Lua.luaL_loadbuffer(ctxluaState, chunk, (uint)chunk.Length, chunkName) == 0)
            {
                try
                {
                    if (Lua.lua_pcall(ctxluaState, 0, -1, 0) == 0)
                        return ctx.translator.popValues(ctxluaState, oldTop);
                    else
                        ThrowExceptionFromError(ctxluaState, ctx, oldTop);
                }
                finally { ctx.executing = false; }
            }
            else
                ThrowExceptionFromError(ctxluaState, ctx, oldTop);

            return null;            // Never reached - keeps compiler happy
        }

        private static int traceback(Lua.lua_State luaState)
        {
            Lua.lua_getglobal(luaState,"debug");
            Lua.lua_getfield(luaState,-1,"traceback");
            Lua.lua_pushvalue(luaState,1);
            Lua.lua_pushnumber(luaState,2);
            Lua.lua_call (luaState,2,1);
            return 1;
        }

        /*
         * Excutes a Lua file and returns all the chunk's return
         * values in an array
         */
        public static object[] DoFile(Lua.lua_State ctxluaState, string fileName)
        {
        	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");  
            Lua.lua_pushcfunction(ctxluaState, ctx.tracebackFunction);
            int oldTop=Lua.lua_gettop(ctxluaState);
            if(Lua.luaL_loadfile(ctxluaState,fileName)==0)
            {
                ctx.executing = true;
                try
                {
                    if (Lua.lua_pcall(ctxluaState, 0, -1, -2) == 0)
                        return ctx.translator.popValues(ctxluaState, oldTop);
                    else
                        ThrowExceptionFromError(ctxluaState, ctx, oldTop);
                }
                finally { ctx.executing = false; }
            }
            else
                ThrowExceptionFromError(ctxluaState, ctx, oldTop);

            return null;            // Never reached - keeps compiler happy
        }


        /*
         * Indexer for global variables from the LuaInterpreter
         * Supports navigation of tables by using . operator
         */
        public static object get(Lua.lua_State ctxluaState, string fullPath)
        {
        	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");   
            object returnValue = null;
            int oldTop = Lua.lua_gettop(ctxluaState);
            string[] path = fullPath.Split(new char[] { '.' });
            Lua.lua_getglobal(ctxluaState, path[0]);
            returnValue = ctx.translator.getObject(ctxluaState, -1);
            if (path.Length > 1)
            {
                string[] remainingPath = new string[path.Length - 1];
                Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
                returnValue = getObject(ctxluaState, ctx, remainingPath);
            }
            Lua.lua_settop(ctxluaState, oldTop);
            return returnValue;
         }
         
        public static void set(Lua.lua_State ctxluaState, string fullPath, object value)
        {
        	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");        	
            int oldTop = Lua.lua_gettop(ctxluaState);
            string[] path = fullPath.Split(new char[] { '.' });
            if (path.Length == 1)
            {
                ctx.translator.push(ctxluaState, value);
                Lua.lua_setglobal(ctxluaState, fullPath);
            }
            else
            {
                Lua.lua_getglobal(ctxluaState, path[0]);
                string[] remainingPath = new string[path.Length - 1];
                Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
                setObject(ctxluaState, ctx, remainingPath, value);
            }
            Lua.lua_settop(ctxluaState, oldTop);
        }
        

        /// <summary>
        /// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance
        /// </summary>
        /// <remarks>Members of globals are also listed. The formatting is optimized for text input auto-completion.</remarks>
        public static IEnumerable<string> getGlobals(LuaIContext ctx)
        {
            if (!ctx.globalsSorted)
            {
                ctx.globals.Sort();
                ctx.globalsSorted = true;
            }

            return ctx.globals;
        }

        /// <summary>
        /// Adds an entry to <see cref="globals"/> (recursivley handles 2 levels of members)
        /// </summary>
        /// <param name="path">The index accessor path ot the entry</param>
        /// <param name="type">The type of the entry</param>
        /// <param name="recursionCounter">How deep have we gone with recursion?</param>
        private static void registerGlobal(LuaIContext ctx, string path, Type type, int recursionCounter)
        {
            // If the type is a global method, list it directly
            if (type == typeof(Lua.lua_CFunction))
            {
                // Format for easy method invocation
                ctx.globals.Add(path + "(");
            }
            // If the type is a class or an interface and recursion hasn't been running too long, list the members
            else if ((type.IsClass || type.IsInterface) && type != typeof(string) && recursionCounter < 2)
            {
                foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (
                        // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
                        (method.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
                        (method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0) &&
                        // Exclude some generic .NET methods that wouldn't be very usefull in Lua
                        method.Name != "GetType" && method.Name != "GetHashCode" && method.Name != "Equals" &&
                        method.Name != "ToString" && method.Name != "Clone" && method.Name != "Dispose" &&
                        method.Name != "GetEnumerator" && method.Name != "CopyTo" &&
                        !method.Name.StartsWith("get_", StringComparison.Ordinal) &&
                        !method.Name.StartsWith("set_", StringComparison.Ordinal) &&
                        !method.Name.StartsWith("add_", StringComparison.Ordinal) &&
                        !method.Name.StartsWith("remove_", StringComparison.Ordinal))
                    {
                        // Format for easy method invocation
                        string command = path + ":" + method.Name + "(";
                        if (method.GetParameters().Length == 0) command += ")";
                        ctx.globals.Add(command);
                    }
                }
                
                foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (
                        // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
                        (field.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
                        (field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0))
                    {
                        // Go into recursion for members
                        registerGlobal(ctx, path + "." + field.Name, field.FieldType, recursionCounter + 1);
                    }
                }
                
                foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (
                        // Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
                        (property.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
                        (property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0)
                        // Exclude some generic .NET properties that wouldn't be very usefull in Lua
                        && property.Name != "Item")
                    {
                        // Go into recursion for members
                        registerGlobal(ctx, path + "." + property.Name, property.PropertyType, recursionCounter + 1);
                    }
                }
            }
            // Otherwise simply add the element to the list
            else ctx.globals.Add(path);

            // List will need to be sorted on next access
            ctx.globalsSorted = false;
        }
        
        /*
         * Navigates a table in the top of the stack, returning
         * the value of the specified field
         */
        internal static object getObject(Lua.lua_State ctxluaState, LuaIContext ctx, string[] remainingPath)
        {
            object returnValue=null;
            for(int i=0;i<remainingPath.Length;i++)
            {
                Lua.lua_pushstring(ctxluaState,remainingPath[i]);
                Lua.lua_gettable(ctxluaState,-2);
                returnValue=ctx.translator.getObject(ctxluaState,-1);
                if(returnValue==null) break;
            }
            return returnValue;
        }
        /*
         * Gets a numeric global variable
         */
        public static double GetNumber(Lua.lua_State ctxluaState, string fullPath)
        {
        	return (double)get(ctxluaState, fullPath);
        }
        /*
         * Gets a string global variable
         */
        public static string GetString(Lua.lua_State ctxluaState, string fullPath)
        {
        	return (string)get(ctxluaState, fullPath);
        }
        /*
         * Gets a table global variable
         */
        public static LuaTable GetTable(Lua.lua_State ctxluaState, string fullPath)
        {
        	return (LuaTable)get(ctxluaState, fullPath);
        }
        /*
         * Gets a table global variable as an object implementing
         * the interfaceType interface
         */
        public static object GetTable(Lua.lua_State ctxluaState, Type interfaceType, string fullPath)
        {
            return CodeGeneration.Instance.GetClassInstance(interfaceType,GetTable(ctxluaState, fullPath));
        }
        /*
         * Gets a function global variable
         */
        public static LuaFunction GetFunction(Lua.lua_State ctxluaState, LuaIContext ctx, string fullPath)
        {
        	object obj = get(ctxluaState, fullPath);
            return (obj is Lua.lua_CFunction ? new LuaFunction((Lua.lua_CFunction)obj, ctx) : (LuaFunction)obj);
        }
        /*
         * Gets a function global variable as a delegate of
         * type delegateType
         */
        public static Delegate GetFunction(Lua.lua_State ctxluaState, LuaIContext ctx, Type delegateType,string fullPath)
        {
            return CodeGeneration.Instance.GetDelegate(delegateType, GetFunction(ctxluaState, ctx, fullPath));
        }
        /*
         * Calls the object as a function with the provided arguments,
         * returning the function's returned values inside an array
         */
        internal static object[] callFunction(Lua.lua_State ctxluaState, LuaIContext ctx, object function,object[] args)
        {
            return callFunction(ctxluaState, ctx, function, args, null);
        }


        /*
         * Calls the object as a function with the provided arguments and
         * casting returned values to the types in returnTypes before returning
         * them in an array
         */
        internal static object[] callFunction(Lua.lua_State ctxluaState, LuaIContext ctx, object function,object[] args,Type[] returnTypes)
        {
            int nArgs=0;
            int oldTop=Lua.lua_gettop(ctxluaState);
            if(Lua.lua_checkstack(ctxluaState,args.Length+6)==0)
                throw new LuaException("Lua stack overflow");
            ctx.translator.push(ctxluaState,function);
            if(args!=null)
            {
                nArgs=args.Length;
                for(int i=0;i<args.Length;i++)
                {
                    ctx.translator.push(ctxluaState,args[i]);
                }
            }
            ctx.executing = true;
            try
            {
                int error = Lua.lua_pcall(ctxluaState, nArgs, -1, 0);
                if (error != 0)
                    ThrowExceptionFromError(ctxluaState, ctx, oldTop);
            }
            finally { ctx.executing = false; }

            if(returnTypes != null)
                return ctx.translator.popValues(ctxluaState, oldTop, returnTypes);
            else
                return ctx.translator.popValues(ctxluaState, oldTop);
        }
        /*
         * Navigates a table to set the value of one of its fields
         */
        internal static void setObject(Lua.lua_State ctxluaState, LuaIContext ctx, string[] remainingPath, object val)
        {
            for(int i=0; i<remainingPath.Length-1;i++)
            {
                Lua.lua_pushstring(ctxluaState,remainingPath[i]);
                Lua.lua_gettable(ctxluaState,-2);
            }
            Lua.lua_pushstring(ctxluaState,remainingPath[remainingPath.Length-1]);
            ctx.translator.push(ctxluaState,val);
            Lua.lua_settable(ctxluaState,-3);
        }
        /*
         * Creates a new table as a global variable or as a field
         * inside an existing table
         */
        public static void NewTable(Lua.lua_State ctxluaState, string fullPath)
        {
            string[] path=fullPath.Split(new char[] { '.' });
            int oldTop=Lua.lua_gettop(ctxluaState);
            if(path.Length==1)
            {
                Lua.lua_newtable(ctxluaState);
                Lua.lua_setglobal(ctxluaState,fullPath);
            }
            else
            {
                Lua.lua_getglobal(ctxluaState,path[0]);
                for(int i=1; i<path.Length-1;i++)
                {
                    Lua.lua_pushstring(ctxluaState,path[i]);
                    Lua.lua_gettable(ctxluaState,-2);
                }
                Lua.lua_pushstring(ctxluaState,path[path.Length-1]);
                Lua.lua_newtable(ctxluaState);
                Lua.lua_settable(ctxluaState,-3);
            }
            Lua.lua_settop(ctxluaState,oldTop);
        }

        public static ListDictionary GetTableDict(Lua.lua_State ctxluaState, LuaIContext ctx, LuaTable table)
        {
            ListDictionary dict = new ListDictionary();

            int oldTop = Lua.lua_gettop(ctxluaState);
            ctx.translator.push(ctxluaState, table);
            Lua.lua_pushnil(ctxluaState);
            while (Lua.lua_next(ctxluaState, -2) != 0)
            {
                dict[ctx.translator.getObject(ctxluaState, -2)] = ctx.translator.getObject(ctxluaState, -1);
                Lua.lua_settop(ctxluaState, -2);
            }
            Lua.lua_settop(ctxluaState, oldTop);

            return dict;
        }

        /*
         * Lets go of a previously allocated reference to a table, function
         * or userdata
         */

        internal static void dispose(Lua.lua_State ctxluaState, int reference)
        {
            if (ctxluaState != null) //Fix submitted by Qingrui Li
                Lua.lua_unref(ctxluaState,reference);
        }
        /*
         * Gets a field of the table corresponding to the provided reference
         * using rawget (do not use metatables)
         */
        internal static object rawGetObject(Lua.lua_State ctxluaState, LuaIContext ctx, int reference,string field)
        {
            int oldTop=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,reference);
            Lua.lua_pushstring(ctxluaState,field);
            Lua.lua_rawget(ctxluaState,-2);
            object obj=ctx.translator.getObject(ctxluaState,-1);
            Lua.lua_settop(ctxluaState,oldTop);
            return obj;
        }
        /*
         * Gets a field of the table or userdata corresponding to the provided reference
         */
        internal static object getObject(Lua.lua_State ctxluaState, LuaIContext ctx, int reference,string field)
        {
            int oldTop=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,reference);
            object returnValue=getObject(ctxluaState, ctx, field.Split(new char[] {'.'}));
            Lua.lua_settop(ctxluaState,oldTop);
            return returnValue;
        }
        /*
         * Gets a numeric field of the table or userdata corresponding the the provided reference
         */
        internal static object getObject(Lua.lua_State ctxluaState, LuaIContext ctx, int reference, object field)
        {
            int oldTop=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,reference);
            ctx.translator.push(ctxluaState,field);
            Lua.lua_gettable(ctxluaState,-2);
            object returnValue=ctx.translator.getObject(ctxluaState,-1);
            Lua.lua_settop(ctxluaState,oldTop);
            return returnValue;
        }
        /*
         * Sets a field of the table or userdata corresponding the the provided reference
         * to the provided value
         */
        internal static void setObject(Lua.lua_State ctxluaState, LuaIContext ctx, int reference, string field, object val)
        {
            int oldTop=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,reference);
            setObject(ctxluaState, ctx, field.Split(new char[] {'.'}),val);
            Lua.lua_settop(ctxluaState,oldTop);
        }
        /*
         * Sets a numeric field of the table or userdata corresponding the the provided reference
         * to the provided value
         */
        internal static void setObject(Lua.lua_State ctxluaState, LuaIContext ctx, int reference, object field, object val)
        {
            int oldTop=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,reference);
            ctx.translator.push(ctxluaState,field);
            ctx.translator.push(ctxluaState,val);
            Lua.lua_settable(ctxluaState,-3);
            Lua.lua_settop(ctxluaState,oldTop);
        }

        /*
         * Registers an object's method as a Lua function (global or table field)
         * The method may have any signature
         */
        public static LuaFunction RegisterFunction(Lua.lua_State ctxluaState, string path, object target, MethodBase function /*MethodInfo function*/)  //CP: Fix for struct constructor by Alexander Kappner (link: http://luaforge.net/forum/forum.php?thread_id=2859&forum_id=145)
        {
         	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");
            // We leave nothing on the stack when we are done
            int oldTop = Lua.lua_gettop(ctxluaState);

            LuaMethodWrapper wrapper=new LuaMethodWrapper(ctxluaState, ctx.translator,target,function.DeclaringType,function);
            ctx.translator.push(ctxluaState, new Lua.lua_CFunction(wrapper.call));

            set(ctxluaState, path, ctx.translator.getObject(ctxluaState,-1));
            LuaFunction f = GetFunction(ctxluaState, ctx, path);

            Lua.lua_settop(ctxluaState, oldTop);

            return f;
        }


        /*
         * Compares the two values referenced by ref1 and ref2 for equality
         */
        internal static bool compareRef(Lua.lua_State ctxluaState, int ref1, int ref2)
        {
            int top=Lua.lua_gettop(ctxluaState);
            Lua.lua_getref(ctxluaState,ref1);
            Lua.lua_getref(ctxluaState,ref2);
            int equal=Lua.lua_equal(ctxluaState,-1,-2);
            Lua.lua_settop(ctxluaState, top);
            return (equal!=0);
        }

        internal static void pushCSFunction(Lua.lua_State ctxluaState, LuaIContext ctx, Lua.lua_CFunction function)
        {
            ctx.translator.pushFunction(ctxluaState, function);
        }


        public static void Dispose(Lua.lua_State ctxluaState)
        {
         	LuaIContext ctx = LuaI.getLuaIContext(ctxluaState);
            if (ctx == null) throw new ArgumentNullException("ctx");
            if (ctx.translator != null)
            {
                ctx.translator.pendingEvents.Dispose();
                ctx.translator = null;
            }

            Close(ctxluaState, ctx);
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
        }

   }



}
